{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 协程\n",
    "- 参考资料\n",
    "    - http://python.jobbole.com/86481/\n",
    "    - http://python.jobbole.com/87310/\n",
    "    - https://segmentfault.com/a/1190000009781688\n",
    "        \n",
    "# 迭代器\n",
    "- 可迭代(Iterable):直接作用于for循环的变量\n",
    "- 迭代器(Iterator):不但可以作用于for循环，还可以被next调用\n",
    "- list是典型的可迭代对象，但不是迭代器\n",
    "- 通过isinstance判断\n",
    "- iterable和iterator可以转换\n",
    "    - 通过iter函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n",
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "# 可迭代\n",
    "l = [ i for i in range(10)]\n",
    "\n",
    "# l是可迭代的，但不是迭代器\n",
    "for idx in l:\n",
    "    print(idx)\n",
    "\n",
    "# range是个迭代器\n",
    "for i in range(5):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "# isinstance案例\n",
    "# 判断某个变量是否是一个实例\n",
    "\n",
    "# 判断是否课可迭代\n",
    "from collections import Iterable\n",
    "ll = [1,2,3,4,5]\n",
    "\n",
    "print(isinstance(ll, Iterable))\n",
    "\n",
    "from collections import Iterator\n",
    "print(isinstance(ll, Iterator))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "# iter函数\n",
    "\n",
    "s = 'i love wangxiaojign'\n",
    "\n",
    "print(isinstance(s, Iterable))\n",
    "print(isinstance(s, Iterator))\n",
    "\n",
    "s_iter = iter(s)\n",
    "print(isinstance(s_iter, Iterable))\n",
    "print(isinstance(s_iter, Iterator))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 生成器\n",
    "- generator: 一边循环一边计算下一个元素的机制/算法\n",
    "- 需要满足三个条件：\n",
    "    - 每次调用都生产出for循环需要的下一个元素或者\n",
    "    - 如果达到最后一个后，爆出StopIteration异常\n",
    "    - 可以被next函数调用\n",
    "- 如何生成一个生成器\n",
    "    - 直接使用\n",
    "    - 如果函数中包含yield，则这个函数就叫生成器\n",
    "    - next调用函数，遇到yield返回"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'list'>\n",
      "<class 'generator'>\n"
     ]
    }
   ],
   "source": [
    "# 直接使用生成器\n",
    "\n",
    "L = [x*x for x in range(5)] # 放在中括号中是列表生成器\n",
    "g = (x*x for x in range(5))#  放在小括号中就是生成器\n",
    "\n",
    "print(type(L))\n",
    "print(type(g))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 1\n",
      "Step 2\n",
      "Step 3\n"
     ]
    }
   ],
   "source": [
    "# 函数案例\n",
    "\n",
    "def odd():\n",
    "    print(\"Step 1\")\n",
    "    print(\"Step 2\")\n",
    "    print(\"Step 3\")\n",
    "    return None\n",
    "\n",
    "odd()\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step 1\n",
      "1\n",
      "Step 2\n",
      "2\n",
      "Step 3\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "# 生成器的案例\n",
    "# 在函数odd中，yield负责返回\n",
    "def odd():\n",
    "    print(\"Step 1\")\n",
    "    yield 1\n",
    "    print(\"Step 2\")\n",
    "    yield 2\n",
    "    print(\"Step 3\")\n",
    "    yield 3\n",
    " \n",
    "# odd() 是调用生成器\n",
    "g = odd()\n",
    "one = next(g)\n",
    "print(one)\n",
    "\n",
    "two = next(g)\n",
    "print(two)\n",
    "\n",
    "three = next(g)\n",
    "print(three)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Done'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# for循环调用生成器\n",
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1 # 注意写法\n",
    "    while n < max:\n",
    "        print(b)\n",
    "        a,b = b, a+b # 注意写法\n",
    "        n += 1\n",
    "        \n",
    "    return 'Done'\n",
    "\n",
    "fib(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "Done",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-19-51ae2dbd94ff>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     12\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     13\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m6\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 14\u001b[1;33m     \u001b[0mrst\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     15\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mrst\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mStopIteration\u001b[0m: Done"
     ]
    }
   ],
   "source": [
    "# 斐波那契额数列的生成器写法\n",
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1 # 注意写法\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a,b = b, a+b # 注意写法\n",
    "        n += 1\n",
    "        \n",
    "    #需要注意，爆出异常是的返回值是return的返回值\n",
    "    return 'Done'\n",
    "\n",
    "g = fib(5)\n",
    "\n",
    "for i in range(6):\n",
    "    rst = next(g)\n",
    "    print(rst)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n",
      "13\n",
      "21\n",
      "34\n",
      "55\n"
     ]
    }
   ],
   "source": [
    "ge = fib(10)\n",
    "'''\n",
    "生成器的典型用法是在for中使用\n",
    "比较常用的典型生成器就是range\n",
    "'''\n",
    "for i in ge: #在for循环中使用生成器\n",
    "    print(i)\n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 协程\n",
    "- 历史历程\n",
    "    - 3.4引入协程，用yield实现\n",
    "    - 3.5引入协程语法\n",
    "    - 实现的协程比较好的包有asyncio, tornado, gevent\n",
    "- 定义：协程 是为非抢占式多任务产生子程序的计算机程序组件，协程允许不同入口点在不同位置暂停或开始执行程序”。\n",
    "- 从技术角度讲，协程就是一个你可以暂停执行的函数，或者干脆把协程理解成生成器\n",
    "- 协程的实现：\n",
    "    - yield返回\n",
    "    - send调用\n",
    " \n",
    "- 协程的四个状态\n",
    "    - inspect.getgeneratorstate(…) 函数确定，该函数会返回下述字符串中的一个：\n",
    "    - GEN_CREATED：等待开始执行\n",
    "    - GEN_RUNNING：解释器正在执行\n",
    "    - GEN_SUSPENED：在yield表达式处暂停\n",
    "    - GEN_CLOSED：执行结束\n",
    "    - next预激（prime)\n",
    "    - 代码案例v2\n",
    "- 协程终止\n",
    "    - 协程中未处理的异常会向上冒泡，传给 next 函数或 send 方法的调用方（即触发协程的对象）\n",
    "    - 止协程的一种方式：发送某个哨符值，让协程退出。内置的 None 和Ellipsis 等常量经常用作哨符值==。\n",
    " \n",
    "- yield from\n",
    "    - 调用协程为了得到返回值，协程必须正常终止\n",
    "    - 生成器正常终止会发出StopIteration异常，异常对象的vlaue属性保存返回值\n",
    "    - yield from从内部捕获StopIteration异常\n",
    "    - 案例v03\n",
    "    - 委派生成器\n",
    "        - 包含yield from表达式的生成器函数\n",
    "        - 委派生成器在yield from表达式出暂停，调用方可以直接把数据发给自生成器\n",
    "        - 子生成器在把产出的值发给调用放\n",
    "        - 自生成器在最后，解释器会抛出StopIteration，并且把返回值附加到异常对象上\n",
    "        - 案例v04"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1111\n",
      "-> start\n",
      "2222\n",
      "-> recived zhexiao\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-21-6e06494028b2>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     12\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     13\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2222\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 14\u001b[1;33m \u001b[0msc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'zhexiao'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 协程代码案例1\n",
    "def simple_coroutine():\n",
    "    print('-> start')\n",
    "    x = yield\n",
    "    print('-> recived', x)\n",
    "\n",
    "#主线程\n",
    "sc = simple_coroutine()\n",
    "print(1111)\n",
    "# 可以使用sc.send(None)，效果一样\n",
    "next(sc) #预激\n",
    "\n",
    "print(2222)\n",
    "sc.send('zhexiao')\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-> start\n",
      "5\n",
      "-> recived 5 6\n",
      "11\n",
      "-> recived 5 6 7\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-22-46f46d949b9d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m     16\u001b[0m \u001b[0mbb\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m6\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 5, 6\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     17\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbb\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 18\u001b[1;33m \u001b[0mcc\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msc\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m7\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 5, 6, 7\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     19\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcc\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     20\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 案例v2，  协程的状态\n",
    "def simple_coroutine(a):\n",
    "    print('-> start')\n",
    "\n",
    "    b = yield a\n",
    "    print('-> recived', a, b)\n",
    "\n",
    "    c = yield a + b\n",
    "    print('-> recived', a, b, c)\n",
    "\n",
    "# runc\n",
    "sc = simple_coroutine(5)\n",
    "\n",
    "aa = next(sc)\n",
    "print(aa)\n",
    "bb = sc.send(6) # 5, 6\n",
    "print(bb)\n",
    "cc = sc.send(7) # 5, 6, 7\n",
    "print(cc)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['A', 'B']\n",
      "['A', 'B']\n"
     ]
    }
   ],
   "source": [
    "# 案例v03\n",
    "def gen():\n",
    "    for c in 'AB':\n",
    "        yield c\n",
    "# list直接用生成器作为参数\n",
    "print(list(gen()))\n",
    "\n",
    "def gen_new():\n",
    "    yield from 'AB'\n",
    "\n",
    "print(list(gen_new()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 案例v04， 委派生成器\n",
    "from collections import namedtuple\n",
    "\n",
    "'''\n",
    "解释：\n",
    "1. 外层 for 循环每次迭代会新建一个 grouper 实例，赋值给 coroutine 变量； grouper 是委派生成器。\n",
    "2. 调用 next(coroutine)，预激委派生成器 grouper，此时进入 while True 循环，调用子生成器 averager 后，在 yield from 表达式处暂停。\n",
    "3. 内层 for 循环调用 coroutine.send(value)，直接把值传给子生成器 averager。同时，当前的 grouper 实例（coroutine）在 yield from 表达式处暂停。\n",
    "4. 内层循环结束后， grouper 实例依旧在 yield from 表达式处暂停，因此， grouper函数定义体中为 results[key] 赋值的语句还没有执行。\n",
    "5. coroutine.send(None) 终止 averager 子生成器，子生成器抛出 StopIteration 异常并将返回的数据包含在异常对象的value中，yield from 可以直接抓取 StopItration 异常并将异常对象的 value 赋值给 results[key]\n",
    "'''\n",
    "ResClass = namedtuple('Res', 'count average')\n",
    "\n",
    "\n",
    "# 子生成器\n",
    "def averager():\n",
    "    total = 0.0\n",
    "    count = 0\n",
    "    average = None\n",
    "\n",
    "    while True:\n",
    "        term = yield\n",
    "        # None是哨兵值\n",
    "        if term is None:\n",
    "            break\n",
    "        total += term\n",
    "        count += 1\n",
    "        average = total / count\n",
    "\n",
    "    return ResClass(count, average)\n",
    "\n",
    "\n",
    "# 委派生成器\n",
    "def grouper(storages, key):\n",
    "    while True:\n",
    "        # 获取averager()返回的值\n",
    "        storages[key] = yield from averager()\n",
    "\n",
    "\n",
    "# 客户端代码\n",
    "def client():\n",
    "    process_data = {\n",
    "        'boys_2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],\n",
    "        'boys_1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46]\n",
    "    }\n",
    "\n",
    "    storages = {}\n",
    "    for k, v in process_data.items():\n",
    "        # 获得协程\n",
    "        coroutine = grouper(storages, k)\n",
    "\n",
    "        # 预激协程\n",
    "        next(coroutine)\n",
    "\n",
    "        # 发送数据到协程\n",
    "        for dt in v:\n",
    "            coroutine.send(dt)\n",
    "\n",
    "        # 终止协程\n",
    "        coroutine.send(None)\n",
    "    print(storages)\n",
    "\n",
    "# run\n",
    "client()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
